import sys
import json
import os
import queue
import copy  # 导入 copy 模块
import threading  # 导入 threading 模块
from PyQt5.QtWidgets import (
    QApplication, QWidget, QLabel, QPushButton, QTextEdit, QHBoxLayout,
    QVBoxLayout, QMainWindow, QMessageBox, QLineEdit, QFileDialog, QScrollArea
)
from PyQt5.QtGui import QPixmap, QFont
from PyQt5.QtCore import Qt, QTimer, QThread, pyqtSignal

class SaveThread(QThread):
    # 自定义线程类，用于保存数据
    finished = pyqtSignal()
    
    def __init__(self, save_queue):
        super().__init__()
        self.save_queue = save_queue
        self.is_running = True  # 线程运行标志

    def run(self):
        while True:
            item = self.save_queue.get()
            if item is None:
                self.save_queue.task_done()
                break  # 收到退出信号，跳出循环
            temp_data_list, temp_file_path = item
            try:
                # 执行保存操作
                with open(temp_file_path, 'w', encoding='utf-8') as f:
                    json.dump(temp_data_list, f, ensure_ascii=False, indent=4)
                self.finished.emit()
            except Exception as e:
                print(f"保存线程出错：{e}")
            finally:
                self.save_queue.task_done()
        print("保存线程已停止")

    def stop(self):
        self.save_queue.put(None)  # 放入特殊值通知线程退出

class DataReviewer(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("数据审核器")
        self.current_index = 0
        self.data_list = []
        self.temp_data_list = []
        self.data_file_path = ''
        self.temp_file_path = ''
        self.new_data_file_path = ''
        self.save_timer = QTimer()
        self.save_timer.setSingleShot(True)
        self.save_delay = 3000  # 延迟3秒
        self.save_queue = queue.Queue()  # 创建保存队列
        self.save_thread = SaveThread(self.save_queue)  # 创建保存线程
        self.save_thread.start()  # 启动保存线程
        self.data_lock = threading.Lock()  # 创建线程锁
        self.unsaved_changes = False  # 标记是否有未保存的更改
        self.select_data_file()
        self.load_data()
        self.init_ui()
        self.jump_to_unmarked()
        self.update_display()

    def closeEvent(self, event):
        # 在关闭窗口时，确保所有数据已保存
        self.save_timer.stop()
        self.delayed_save_temp_data()
        # 等待保存队列处理完所有任务
        self.save_queue.join()
        # 然后停止保存线程
        self.save_thread.stop()
        self.save_thread.wait()
        event.accept()

    def select_data_file(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        file_name, _ = QFileDialog.getOpenFileName(
            self, "选择数据文件(不要选择后缀_temp结尾的，这些是临时文件)", "", "JSON Files (*.json);;All Files (*)", options=options)
        if file_name:
            self.data_file_path = file_name
            # 生成 temp.json 文件名
            base_name = os.path.basename(file_name)
            name_without_ext = os.path.splitext(base_name)[0]
            self.temp_file_path = os.path.join(
                os.path.dirname(file_name), f"{name_without_ext}_temp.json")
            # 设置提取后的新数据文件名
            self.new_data_file_path = os.path.join(
                os.path.dirname(file_name), f"new_{base_name}")
        else:
            QMessageBox.warning(self, "警告", "未选择数据文件，程序即将退出")
            sys.exit()

    def load_data(self):
        try:
            if os.path.exists(self.temp_file_path):
                # 加载 temp.json
                with open(self.temp_file_path, 'r', encoding='utf-8') as f:
                    self.temp_data_list = json.load(f)
            else:
                # 加载选定的 data.json 并初始化 temp_data_list
                with open(self.data_file_path, 'r', encoding='utf-8') as f:
                    self.data_list = json.load(f)
                # 初始化 temp_data_list，并添加 'choose' 字段
                self.temp_data_list = []
                for item in self.data_list:
                    temp_item = item.copy()
                    temp_item['choose'] = ""
                    self.temp_data_list.append(temp_item)
        except Exception as e:
            QMessageBox.critical(self, "错误", f"加载数据时发生错误：{e}")
            sys.exit()

    def init_ui(self):
        self.resize(900, 600)
        self.setMinimumSize(800, 600)  # 设置最小尺寸
        # 设置全局字体
        font = QFont('微软雅黑', 10)
        self.setFont(font)

        # 中央窗口部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)

        # 图片容器，用于叠加指示灯
        self.image_container = QWidget()
        # self.image_container.setFixedSize(400, 400)  # 移除固定大小
        self.image_container_layout = QVBoxLayout()
        self.image_container_layout.setContentsMargins(0, 0, 0, 0)
        self.image_container_layout.setSpacing(0)
        self.image_container.setLayout(self.image_container_layout)

        # 图片区域，用于放置多个图片
        self.scroll_area = QScrollArea()
        self.scroll_area.setWidgetResizable(True)
        self.scroll_area_widget = QWidget()
        self.scroll_area_layout = QVBoxLayout()
        self.scroll_area_layout.setContentsMargins(0, 0, 0, 0)
        self.scroll_area_layout.setSpacing(5)
        self.scroll_area_widget.setLayout(self.scroll_area_layout)
        self.scroll_area.setWidget(self.scroll_area_widget)
        # self.scroll_area.setFixedSize(400, 400)  # 移除固定大小

        self.image_container_layout.addWidget(self.scroll_area)

        # 指示灯（放在图片左上角）
        self.indicator_label = QLabel(self.image_container)
        self.indicator_label.setFixedSize(30, 30)
        self.indicator_label.move(10, 10)
        self.indicator_label.setAlignment(Qt.AlignCenter)
        self.indicator_label.setStyleSheet("""
            background-color: white;
            border: 2px solid #D3D3D3;
            border-radius: 15px;
        """)

        # 文本显示
        self.text_edit = QTextEdit()
        # self.text_edit.setFixedSize(400, 400)  # 移除固定大小
        self.text_edit.setStyleSheet("""
            QTextEdit {
                font-size: 16px;
                border: 2px solid #D3D3D3;
                background-color: #FFFFFF;
            }
        """)
        # 连接文本变化信号到槽函数
        self.text_edit.textChanged.connect(self.on_text_changed)

        # 布局
        h_layout = QHBoxLayout()
        h_layout.addWidget(self.image_container)
        h_layout.addWidget(self.text_edit)
        h_layout.setStretch(0, 1)  # 设置伸缩比例
        h_layout.setStretch(1, 1)

        # 导航按钮
        prev_button = QPushButton("上一条 (A)")
        prev_button.clicked.connect(self.prev_item)
        prev_button.setShortcut('A')
        prev_button.setStyleSheet(self.button_style())

        next_button = QPushButton("下一条 (D)")
        next_button.clicked.connect(self.next_item)
        next_button.setShortcut('D')
        next_button.setStyleSheet(self.button_style())

        # 选择按钮
        keep_button = QPushButton("√ (J)")
        keep_button.setStyleSheet(self.button_style("#4CAF50"))
        keep_button.clicked.connect(self.keep_item)
        keep_button.setShortcut('J')

        discard_button = QPushButton("× (L)")
        discard_button.setStyleSheet(self.button_style("#F44336"))
        discard_button.clicked.connect(self.discard_item)
        discard_button.setShortcut('L')

        # 提取数据按钮
        extract_button = QPushButton("提取数据")
        extract_button.clicked.connect(self.extract_data)
        extract_button.setStyleSheet(self.button_style("#2196F3"))

        # 重新导入数据按钮
        reload_button = QPushButton("重新导入数据")
        reload_button.clicked.connect(self.reload_data)
        reload_button.setStyleSheet(self.button_style("#FF9800"))

        # 跳转功能
        self.jump_input = QLineEdit()
        self.jump_input.setPlaceholderText("输入要跳转的编号")
        self.jump_input.setFixedWidth(120)
        self.jump_input.returnPressed.connect(self.jump_to_index)

        jump_button = QPushButton("跳转")
        jump_button.clicked.connect(self.jump_to_index)
        jump_button.setStyleSheet(self.button_style())

        # 进度条
        self.progress_label = QLabel()
        self.progress_label.setAlignment(Qt.AlignCenter)
        self.progress_label.setStyleSheet("font-size: 14px;")

        # 按钮布局
        buttons_layout = QHBoxLayout()
        buttons_layout.addWidget(prev_button)
        buttons_layout.addWidget(next_button)
        buttons_layout.addStretch()
        buttons_layout.addWidget(keep_button)
        buttons_layout.addWidget(discard_button)
        buttons_layout.addStretch()
        buttons_layout.addWidget(self.jump_input)
        buttons_layout.addWidget(jump_button)
        buttons_layout.addStretch()
        buttons_layout.addWidget(extract_button)
        buttons_layout.addWidget(reload_button)

        # 主布局
        main_layout = QVBoxLayout()
        main_layout.addLayout(h_layout)
        main_layout.addLayout(buttons_layout)
        main_layout.addWidget(self.progress_label)

        central_widget.setLayout(main_layout)

        # 连接 QTimer 的超时信号到保存函数
        self.save_timer.timeout.connect(self.delayed_save_temp_data)

    def button_style(self, color="#5F9EA0"):
        return f"""
            QPushButton {{
                background-color: {color};
                border: none;
                color: white;
                padding: 8px 16px;
                text-align: center;
                font-size: 14px;
                border-radius: 4px;
            }}
            QPushButton:hover {{
                background-color: #4682B4;
            }}
        """

    def update_display(self):
        # 更新显示内容
        if 0 <= self.current_index < len(self.temp_data_list):
            item = self.temp_data_list[self.current_index]

            # 清空之前的图片
            for i in reversed(range(self.scroll_area_layout.count())):
                widgetToRemove = self.scroll_area_layout.itemAt(i).widget()
                self.scroll_area_layout.removeWidget(widgetToRemove)
                widgetToRemove.setParent(None)

            # 加载图片
            data_file_dir = os.path.dirname(self.data_file_path)
            image_paths = []

            # 检查 'image' 字段
            if 'image' in item and isinstance(item['image'], str) and item['image']:
                image_paths.append(item['image'])
            # 检查 'images' 字段
            if 'images' in item and isinstance(item['images'], list) and item['images']:
                image_paths.extend(item['images'])

            if image_paths:
                for image_path in image_paths:
                    full_image_path = os.path.join(data_file_dir, image_path)
                    image_label = QLabel()
                    # image_label.setFixedSize(380, 380)  # 移除固定大小
                    image_label.setAlignment(Qt.AlignCenter)
                    image_label.setStyleSheet("""
                        QLabel {
                            border: 1px solid #D3D3D3;
                            background-color: #F5F5F5;
                        }
                    """)
                    if os.path.exists(full_image_path):
                        pixmap = QPixmap(full_image_path)
                        # 根据窗口大小调整图片大小
                        container_width = self.image_container.width()
                        container_height = self.image_container.height()
                        scaled_pixmap = pixmap.scaled(
                            container_width * 0.9, container_height * 0.9, Qt.KeepAspectRatio, Qt.SmoothTransformation)
                        image_label.setPixmap(scaled_pixmap)
                    else:
                        image_label.setText("图片未找到")
                        image_label.setStyleSheet("""
                            QLabel {
                                border: 1px solid #D3D3D3;
                                background-color: #F5F5F5;
                                color: red;
                                font-size: 18px;
                            }
                        """)
                    self.scroll_area_layout.addWidget(image_label)
            else:
                image_label = QLabel()
                # image_label.setFixedSize(380, 380)  # 移除固定大小
                image_label.setAlignment(Qt.AlignCenter)
                image_label.setText("无图片")
                image_label.setStyleSheet("""
                    QLabel {
                        border: 1px solid #D3D3D3;
                        background-color: #F5F5F5;
                        color: gray;
                        font-size: 18px;
                    }
                """)
                self.scroll_area_layout.addWidget(image_label)

            # 显示文本内容
            displayed_text = ""
            # 检查 'conversations' 字段
            if 'conversations' in item and isinstance(item['conversations'], list) and item['conversations']:
                last_conversation = item['conversations'][-1]
                if 'value' in last_conversation:
                    displayed_text = last_conversation['value']
            # 检查 'messages' 字段
            elif 'messages' in item and isinstance(item['messages'], list) and item['messages']:
                last_message = item['messages'][-1]
                if 'content' in last_message:
                    displayed_text = last_message['content']

            if displayed_text:
                # 为了防止 textChanged 信号在 update_display 时被触发，我们暂时断开信号
                self.text_edit.blockSignals(True)
                self.text_edit.setText(displayed_text)
                self.text_edit.blockSignals(False)
            else:
                self.text_edit.setText("无可用内容")

            # 更新指示灯颜色
            choose_value = item.get('choose', "")
            if choose_value == "":
                color = "white"
            elif choose_value == True:
                color = "#4CAF50"  # 绿色
            elif choose_value == False:
                color = "#F44336"  # 红色
            else:
                color = "white"
            self.indicator_label.setStyleSheet(f"""
                background-color: {color};
                border: 2px solid #D3D3D3;
                border-radius: 15px;
            """)

            # 更新进度条
            self.progress_label.setText(
                f"当前进度：第 {self.current_index + 1} 条 / 共 {len(self.temp_data_list)} 条")
        else:
            # 没有数据的情况
            for i in reversed(range(self.scroll_area_layout.count())):
                widgetToRemove = self.scroll_area_layout.itemAt(i).widget()
                self.scroll_area_layout.removeWidget(widgetToRemove)
                widgetToRemove.setParent(None)
            self.text_edit.setText("")
            self.indicator_label.setStyleSheet("""
                background-color: white;
                border: 2px solid #D3D3D3;
                border-radius: 15px;
            """)
            self.progress_label.setText("无数据")

    def resizeEvent(self, event):
        # 当窗口大小改变时，重新加载显示内容，确保图片按比例调整
        self.update_display()
        super().resizeEvent(event)

    def prev_item(self):
        if self.current_index > 0:
            self.current_index -= 1
            self.update_display()
        else:
            QMessageBox.information(self, "提示", "已经是第一条数据了！")

    def next_item(self):
        if self.current_index < len(self.temp_data_list) - 1:
            self.current_index += 1
            self.update_display()
        else:
            QMessageBox.information(self, "提示", "已经是最后一条数据了！")

    def keep_item(self):
        with self.data_lock:
            self.temp_data_list[self.current_index]['choose'] = True
            self.unsaved_changes = True
        # 自动跳到下一条数据
        if self.current_index < len(self.temp_data_list) - 1:
            self.current_index += 1
        else:
            QMessageBox.information(self, "提示", "已经是最后一条数据了！")
        self.update_display()

    def discard_item(self):
        with self.data_lock:
            self.temp_data_list[self.current_index]['choose'] = False
            self.unsaved_changes = True
        # 自动跳到下一条数据
        if self.current_index < len(self.temp_data_list) - 1:
            self.current_index += 1
        else:
            QMessageBox.information(self, "提示", "已经是最后一条数据了！")
        self.update_display()

    def enqueue_save(self):
        # 将保存请求放入队列，使用深拷贝
        with self.data_lock:
            data_copy = copy.deepcopy(self.temp_data_list)
            self.unsaved_changes = False  # 重置未保存更改标志
        self.save_queue.put((data_copy, self.temp_file_path))

    def delayed_save_temp_data(self):
        # 延迟保存数据的函数，使用队列保存
        if self.unsaved_changes:
            self.enqueue_save()

    def extract_data(self):
        with self.data_lock:
            extracted_data = [item for item in self.temp_data_list if item['choose'] == True]
        if extracted_data:
            try:
                # 保存到 new_原json文件名.json
                with open(self.new_data_file_path, 'w', encoding='utf-8') as f:
                    json.dump(extracted_data, f, ensure_ascii=False, indent=4)
                # 显示提示信息
                self.statusBar().showMessage(f"数据已提取到 {self.new_data_file_path}", 5000)
                QMessageBox.information(self, "提示", f"数据已提取到 {self.new_data_file_path}")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"提取数据时发生错误：{e}")
        else:
            QMessageBox.information(self, "提示", "没有选择任何数据进行提取！")

    def reload_data(self):
        reply = QMessageBox.question(
            self, '确认重新导入', '是否重新导入？重新导入会覆盖之前的记录', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            options = QFileDialog.Options()
            options |= QFileDialog.DontUseNativeDialog
            file_name, _ = QFileDialog.getOpenFileName(
                self, "选择数据文件", "", "JSON Files (*.json);;All Files (*)", options=options)
            if file_name:
                self.data_file_path = file_name
                # 生成 temp.json 文件名
                base_name = os.path.basename(file_name)
                name_without_ext = os.path.splitext(base_name)[0]
                self.temp_file_path = os.path.join(
                    os.path.dirname(file_name), f"{name_without_ext}_temp.json")
                # 设置提取后的新数据文件名
                self.new_data_file_path = os.path.join(
                    os.path.dirname(file_name), f"new_{base_name}")
                if os.path.exists(self.data_file_path):
                    try:
                        # 从 data.json 重新加载数据
                        with open(self.data_file_path, 'r', encoding='utf-8') as f:
                            self.data_list = json.load(f)
                        # 初始化 temp_data_list，并添加 'choose' 字段
                        with self.data_lock:
                            self.temp_data_list = []
                            for item in self.data_list:
                                temp_item = item.copy()
                                temp_item['choose'] = ""
                                self.temp_data_list.append(temp_item)
                            self.unsaved_changes = True
                        # 删除 temp.json 文件
                        if os.path.exists(self.temp_file_path):
                            os.remove(self.temp_file_path)
                        self.current_index = 0
                        self.jump_to_unmarked()
                        self.update_display()
                        QMessageBox.information(self, "提示", "数据已重新导入")
                    except Exception as e:
                        QMessageBox.critical(self, "错误", f"重新导入数据时发生错误：{e}")
                else:
                    QMessageBox.warning(self, "警告", f"{self.data_file_path} 文件不存在，无法重新导入")
            else:
                QMessageBox.warning(self, "警告", "未选择数据文件，取消重新导入")
        else:
            pass  # 用户选择 No，不执行任何操作

    def jump_to_unmarked(self):
        # 跳转到第一条未标记的数据
        found_unmarked = False
        with self.data_lock:
            for idx, item in enumerate(self.temp_data_list):
                if item.get('choose', "") == "":
                    self.current_index = idx
                    found_unmarked = True
                    break
            if not found_unmarked:
                # 如果所有数据都已标记，则跳转到最后一条数据
                self.current_index = len(self.temp_data_list) - 1
        self.update_display()

    def jump_to_index(self):
        # 跳转到指定索引
        index_text = self.jump_input.text()
        if index_text.isdigit():
            index = int(index_text) - 1
            if 0 <= index < len(self.temp_data_list):
                self.current_index = index
                self.update_display()
            else:
                QMessageBox.warning(self, "警告", "输入的编号超出范围！")
        else:
            QMessageBox.warning(self, "警告", "请输入有效的数字编号！")

    # 处理文本变化事件
    def on_text_changed(self):
        new_text = self.text_edit.toPlainText()
        with self.data_lock:
            item = self.temp_data_list[self.current_index]
            if 'conversations' in item and isinstance(item['conversations'], list) and item['conversations']:
                last_conversation = item['conversations'][-1]
                last_conversation['value'] = new_text
            elif 'messages' in item and isinstance(item['messages'], list) and item['messages']:
                last_message = item['messages'][-1]
                last_message['content'] = new_text
            self.unsaved_changes = True
        # 每次文本变化，重启计时器
        self.save_timer.stop()
        self.save_timer.start(self.save_delay)

    # 处理键盘事件
    def keyPressEvent(self, event):
        key = event.key()
        if key == Qt.Key_A:
            self.prev_item()
        elif key == Qt.Key_D:
            self.next_item()
        elif key == Qt.Key_J:
            self.keep_item()
        elif key == Qt.Key_L:
            self.discard_item()
        else:
            super().keyPressEvent(event)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    reviewer = DataReviewer()
    reviewer.show()
    sys.exit(app.exec_())
